///////////////////////////////////////////////////////////////////////////////
//
// DBT86 Project : x86 Platform : x86 Interpreter : interp.h
// Copyright (C) 2004 by Ben Hertzberg and Stanford University
// All Rights Reserved
//
// This code may not be used for any purpose without the express written
// consent of the author.  See the LICENSE.txt file for details.
//
//
// DESCRIPTION
//
// This header is used to implement interpretation for x86.
//
///////////////////////////////////////////////////////////////////////////////

#ifndef __X86INTERP_H__
#define __X86INTERP_H__

//
// Global definitions
//
#include "common.h"
#include "config.h"
#include "debug.h"
#include "x86.h"
#include "flags.h"
#include "opprotos.h"
#include "memops.h"

// main entry point for interpreter
void Interp();

void InterpInit();
void InterpDone();

// temporary globals live only during an individual instruction
#ifdef LESS86
extern IVAL newIP;
extern UINT segOverride;
extern UINT fOpSize, fAddrSize;
extern UINT prefixes;
extern UINT mod, bits543, rm, sib;
extern IVAL va, imm0, imm1;
#else
// moved to CPU::tmp to avoid globals
#endif

typedef struct {
  IVAL IP;

  // interpreter state
  UINT fSavedCSFlagD, fSavedSSFlagB;

  // integer state
  IVAL GPR[X86_NUM_GPR+1];

  // flags
  IVAL eflags;
  INT flagsOp;
  IVAL flagsSrc1, flagsSrc2, flagsSrc3;

#if CFG_FPU
  // FPU/MMX/SSE/SSE2
  FP80 FPR[X86_NUM_FPR];
#if defined(CFG_DEBUG) || !defined(LESS86)
  double FPRshadow[X86_NUM_FPR];
#endif
  UINT FPstatus;
  UINT FPcontrol;
  UINT FPtop;
  UINT FPtag;
  UINT SSEscr;
#endif

#ifdef WITH_PIN_SUPPORT
  // limited support for XMM registers
  UINT64 xmm0;
#endif // WITH_PIN_SUPPORT

#if CFG_NATIVE_PROCESS_FS_INTERCEPT
  UINT16 fsSegSelector;
#endif
#if CFG_NATIVE_PROCESS_GS_INTERCEPT
  UINT16 gsSegSelector;
#endif



  // segmentation
  SegmentInfoEntry SegTable[X86_NUM_SEG]; 

} x86Context;

#ifndef LESS86
#include "CPU.h"
#define X86_CTXT(x) curPE->InterpCtxt.x
#define X86_TMP(x) curPE->tmp.x
#else
#if CFG_MULTI_CPU_INTERP
extern UINT CurCtxt, NumCtxt;
extern x86Context *InterpCtxt;
#define X86_CTXT(x) (InterpCtxt[CurCtxt].x)
#define X86_TMP(x) x
#else
extern x86Context InterpCtxt;
#define X86_CTXT(x) (InterpCtxt.x)
#define X86_TMP(x) x
#endif
#endif

#define LOAD_REG_EAX X86_CTXT(GPR[EAX])
#define LOAD_REG_EBX X86_CTXT(GPR[EBX])
#define LOAD_REG_ECX X86_CTXT(GPR[ECX])
#define LOAD_REG_EDX X86_CTXT(GPR[EDX])
#define LOAD_REG_ESI X86_CTXT(GPR[ESI])
#define LOAD_REG_EDI X86_CTXT(GPR[EDI])
#define LOAD_REG_ESP X86_CTXT(GPR[ESP])
#define LOAD_REG_EBP X86_CTXT(GPR[EBP])

#define LOAD_REG_AX (X86_CTXT(GPR[AX]) & 0xFFFF)
#define LOAD_REG_AL (X86_CTXT(GPR[AL]) & 0xFF)

#define LOAD_REG_DWORD_H(reg) X86_CTXT(GPR[(reg)])
#define LOAD_REG_WORD_H(reg) (X86_CTXT(GPR[(reg)]) & 0xFFFF)
#define LOAD_REG_BYTE_H(reg) (((reg) >= 0x4) ? ((X86_CTXT(GPR[(reg)-4]) >> 8) & 0xFF) : (X86_CTXT(GPR[(reg)]) & 0xFF))

#define LOAD_REG_ID_DWORD(dst,reg) { dst = LOAD_REG_DWORD_H(reg); }
#define LOAD_REG_ID_WORD(dst,reg) { dst = LOAD_REG_WORD_H(reg); }
#define LOAD_REG_ID_BYTE(dst,reg) { dst = LOAD_REG_BYTE_H(reg); }
#define STORE_REG_ID_DWORD(reg,val) { X86_CTXT(GPR[(reg)]) = val & IC(0xFFFFFFFF); }
#define STORE_REG_ID_WORD(reg,val) { X86_CTXT(GPR[(reg)]) = (X86_CTXT(GPR[(reg)]) & 0xFFFF0000) | ((val) & 0xFFFF); }
#define STORE_REG_ID_BYTE(reg,val) { X86_CTXT(GPR[((reg) & 0x3)]) = ((reg) > 0x3) ?  \
                                     (X86_CTXT(GPR[((reg) & 0x3)]) & 0xFFFF00FF) | (((val) & 0xFF) << 8) : \
                                     (X86_CTXT(GPR[(reg)]) & 0xFFFFFF00) | ((val) & 0xFF); }

#define LOAD_REG_DWORD LOAD_REG_DWORD_H(X86_TMP(bits543))
#define LOAD_REG_WORD  LOAD_REG_WORD_H(X86_TMP(bits543))
#define LOAD_REG_BYTE  LOAD_REG_BYTE_H(X86_TMP(bits543))
#define LOAD_IMM X86_TMP(imm0)
#define LOAD_IMM_B (X86_TMP(imm0) & IC(0xFF))
#define LOAD_IMM_W (X86_TMP(imm0) & IC(0xFFFF))
#define LOAD_IMM_D (X86_TMP(imm0) & IC(0xFFFFFFFF))
#define LOAD_2ND_IMM X86_TMP(imm1)
#define ADDR_SZ X86_TMP(fAddrSize)
#define NEXT_IP X86_TMP(newIP)
#define PREV_IP X86_CTXT(IP)
#define SEG_OVERRIDE(x) { X86_TMP(segOverride) = (x); }

#define STORE_REG_DWORD(val) STORE_REG_ID_DWORD(X86_TMP(bits543),val)
#define STORE_REG_WORD(val) STORE_REG_ID_WORD(X86_TMP(bits543),val)
#define STORE_REG_BYTE(val) STORE_REG_ID_BYTE(X86_TMP(bits543),val)

#define LOAD_RM_R_BYTE(dst) { (dst) = ((X86_TMP(mod) == 0xC0) ? LOAD_REG_BYTE_H(X86_TMP(rm)) : LOAD_VIRTUAL_BYTE(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ)); }
#define LOAD_RM_R_WORD(dst) { (dst) = ((X86_TMP(mod) == 0xC0) ? LOAD_REG_WORD_H(X86_TMP(rm)) : LOAD_VIRTUAL_UNALIGNED_WORD(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ)); }
#define LOAD_RM_R_DWORD(dst) { (dst) = ((X86_TMP(mod) == 0xC0) ? LOAD_REG_DWORD_H(X86_TMP(rm)) : LOAD_VIRTUAL_UNALIGNED_DWORD(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ)); }
#define LOAD_RM_R_QWORD(dst) { if (X86_TMP(mod) == 0xC0) { ASSERT(UNREACHED); (dst) = 0; } else { (dst) = LOAD_VIRTUAL_UNALIGNED_QWORD(X86_TMP(va),X86_TMP(segOverride),ACCESS_READ); } }
#define LOAD_RM_RW_BYTE(dst) LOAD_RM_R_BYTE(dst)
#define LOAD_RM_RW_WORD(dst) LOAD_RM_R_WORD(dst)
#define LOAD_RM_RW_DWORD(dst) LOAD_RM_R_DWORD(dst)
#define LOAD_RM_RW_QWORD(dst) LOAD_RM_R_QWORD(dst)

#define STORE_RM_BYTE(val) { if (X86_TMP(mod) == 0xC0) { STORE_REG_ID_BYTE(X86_TMP(rm),val); } else { STORE_VIRTUAL_BYTE(val,X86_TMP(va),X86_TMP(segOverride),0); } }
#define STORE_RM_WORD(val) { if (X86_TMP(mod) == 0xC0) { STORE_REG_ID_WORD(X86_TMP(rm),val); } else { STORE_VIRTUAL_UNALIGNED_WORD(val,X86_TMP(va),X86_TMP(segOverride),0); } }
#define STORE_RM_DWORD(val) { if (X86_TMP(mod) == 0xC0) { STORE_REG_ID_DWORD(X86_TMP(rm),val); } else { STORE_VIRTUAL_UNALIGNED_DWORD(val,X86_TMP(va),X86_TMP(segOverride),0); } }
#define STORE_RM_QWORD(val) { if (X86_TMP(mod) == 0xC0) { ASSERT(UNREACHED); } else  { STORE_VIRTUAL_UNALIGNED_QWORD(val,X86_TMP(va),X86_TMP(segOverride),0); } }

#define STORE_RM_W_BYTE(val) STORE_RM_BYTE(val)
#define STORE_RM_W_WORD(val) STORE_RM_WORD(val)
#define STORE_RM_W_DWORD(val) STORE_RM_DWORD(val)

#define LOAD_MEM_DWORD(dst,addr) { (dst) = LOAD_VIRTUAL_UNALIGNED_DWORD((addr),X86_TMP(segOverride),ACCESS_READ); }
#define LOAD_MEM_WORD(dst,addr) { (dst) = LOAD_VIRTUAL_WORD((addr),X86_TMP(segOverride),ACCESS_READ); }
#define LOAD_MEM_BYTE(dst,addr) { (dst) = LOAD_VIRTUAL_BYTE((addr),X86_TMP(segOverride),ACCESS_READ); }
#define STORE_MEM_DWORD(addr,val) { STORE_VIRTUAL_UNALIGNED_DWORD((val),(addr),X86_TMP(segOverride),ACCESS_WRITE); }
#define STORE_MEM_WORD(addr,val) { STORE_VIRTUAL_UNALIGNED_WORD((val),(addr),X86_TMP(segOverride),ACCESS_WRITE); }
#define STORE_MEM_BYTE(addr,val) { STORE_VIRTUAL_BYTE((val),(addr),X86_TMP(segOverride),ACCESS_WRITE); }

#define GET_SEG_SEL GET_SEG_SEL_ID(X86_TMP(bits543))
#define SET_SEG_SEL(val) SET_SEG_SEL_ID(X86_TMP(bits543),val)

#define CHECK_VALID_CS_OFFSET(x)
#define CHECK_STACK_TOP(x)
#define SET_SEG_SEL_ID(seg,val) SetSegSelector(seg,(UINT16)(val))
#define GET_SEG_SEL_ID(seg) GetSegSelector(seg)
#define BOUNDS_ERROR INVALIDOP();
#define DIVIDE_ERROR INVALIDOP();

#if CFG_FPU
void FpuSetControlWord(UINT v);
bool FpuGetReg(UINT idx, void *buf);
#endif

#if CFG_RUNTIME
extern UINT fBlockStart;
#define CONTROL_POINT { fBlockStart = TRUE; }
#else
#define CONTROL_POINT
#endif

#if CFG_NATIVE_PROCESS
extern UINT32 NativeExitAddr;
#endif

#if CFG_DEBUG_INTERP_SEARCH
extern UINT64 DbgInterpSearchCount;
#if CFG_DEBUG
extern UINT DbgInterpSearchBreak;
#endif
#endif

//
// Local definitions
//
#ifdef INCL_INTERP_LOCALS

#endif
#endif
